/* processImage.js
 * 
 * letsface photoscreen image process program
 *
 * Accept photos been uploaded through ftp, running as command line script
 * 'pswatch' is watching the new photos from ftp, will call 'processImage' when
 * a new photo been uploaded.
 *
 * 
 */

var path = require('path');
var winston = require('winston');// our log writer
// write log to file not only to console, using local time
winston.add(winston.transports.File, { filename: path.join(__dirname, "..", "log", "processImage.log"), timestamp: function(){ return new Date().toTimeString()}});

/* the image file must be in format jpg or png
 * the image file as third argments is required
 */
if(process.argv.length < 3 || !/(jpg|jpeg|png)$/i.test(process.argv[2])){
	winston.error("need more argments.")
	return
}
var FILE_PATH = process.argv[2];

//escape empty string, in case there are " " in image path
FILE_PATH = FILE_PATH.replace(/\ /g, "\ ");

var utils = require("./utils");
var append = utils.append;
var generateName = utils.generateName;
var getEventId = utils.getEventId;

/* tried get event id from fourth argment, if failed
 * tried to get event id from the image path
 * event id is required
 */
var EVENT_ID = process.argv[3] ? process.argv[3] : getEventId(FILE_PATH);
if(!EVENT_ID){
	winston.error("can't get event id for " + FILE_PATH);
	return
}
var CONFIG_PATH = path.join(__dirname, "..", "events", EVENT_ID);

var CONFIG;

// defaultConfig is not in tracking in git, should maintaining manually
var defaultConfig = require('./config');
/*
 * try not executing as root user
 */
// switch to possible gid
if(defaultConfig.gid)
	process.setgid(defaultConfig.gid);
// switch to possible uid 
if(defaultConfig.uid)
	process.setuid(defaultConfig.uid);

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require('fs'));
var asyncGm = require('./asyncGm');// a wrapper of gm to make it thenable
var mysql = require('mysql');
var dbConfig = defaultConfig.db;
var emitter = require('events').EventEmitter;

var workFlow = new emitter();
var extName = path.extname(FILE_PATH).toLowerCase();
var extNameRE = /(\.jpg)$/; //new RegExp("\(\\" + extName + "\)$");
var pathName = path.dirname(FILE_PATH);
var tryTimes = 0;
var evProcessComplete = 0;
var NEW_NAME, NEW_NAME_C, NEW_NAME_S, NEW_NAME_QR, watermark, preQrcode, PHOTO_ID;

/*
 * method to add qrcode to each photo after process completed
 */
workFlow.on("processComplete", function(){
	evProcessComplete++;
	if(CONFIG.genQrcode && evProcessComplete > 1){
		preQrcode = require('./preQrcode')();
		preQrcode.process(CONFIG, NEW_NAME, dbConfig, extNameRE);
	}
	// some event has no visual operation so need insert to print table at first place
	if(CONFIG.autoPrint && evProcessComplete > 1 && PHOTO_ID !== undefined){
		var queryString = 'INSERT INTO print_list (event_id, photo_id, copies, is_fullfilled, timestamp) VALUES (?, ?, ?, ?, ?)';
		var params = [EVENT_ID, PHOTO_ID, 1, 0, (new Date).getTime() + ""];
		queryString = mysql.format(queryString, params);
		var connection = mysql.createConnection(dbConfig);
		connection.query(queryString, function(err, res){
			if(err){
				winston.error('Insert data to database failed: ' + err);
			}else{
				winston.info(PHOTO_ID + " been insert to db.");
				connection.destroy();
			}
		});
	}
});
/* 
 * shared method
 * 
 * generate an monochrome style copy of the image
 * (black and white)
 *
 */
workFlow.on("genMono", function(fileName,style){
	var x = CONFIG.thumbnailWidth, 
		y = CONFIG.thumbnailHeihgt,
		z, mono;
	if(style == "p"){
		z = x;
		x = y;
		y = z;
	}
	mono = fileName + "_mono" + path.extname(fileName);
	asyncGm(fileName)
	.command('convert')
	.in("-monochrome")
	.in("-resize", x + "x" + y)
	.writeAsync(mono)
	.then(function(){
		winston.info("generate a monoChrome " + mono);
	})
	.catch(function(err){
		winston.error("genMono error: " + err);
	})
});
/* 
 * shared method
 * 
 * save record to database
 *
 */
workFlow.on("thumbnailReady", function(fileName, style){
	var is_hidden = CONFIG.hide ? CONFIG.hide : 0; 
	var is_portrait = style === "p" ? 1 : 0;
	var timestamp = (new Date).getTime() + "";
	var connection = mysql.createConnection(dbConfig);
	var params = [ path.basename(FILE_PATH), path.basename(fileName), EVENT_ID, is_portrait, timestamp, is_hidden ];
	var sql = 'INSERT INTO photo_list (original_filename, filename, event_id, is_portrait, timestamp, is_hidden) VALUES (?, ?, ?, ?, ?, ?)';
	connection.query(mysql.format(sql, params), function(err, res){
		if(err){
			winston.error('Insert data to database failed: ' + err);
		}else{
			winston.info(fileName + " been recorded in db.");
			PHOTO_ID = res.insertId;
			workFlow.emit('processComplete');
			connection.destroy();
		}
	});
});

/*
 * shared method
 * 
 * by greenscreen
 *
 */
workFlow.on("checkWatermark", function(config, watermarks, filename, style, size, configPath){
	watermark = require("./watermark")();
	watermark.process(config, watermarks, filename, style, size, configPath, workFlow, dbConfig, EVENT_ID, extNameRE);
});

/*
 * 4th step
 * 
 * make thumbnail
 *
 */
workFlow.on("gotSize", function(fileName, size, style){
	var x = CONFIG.thumbnailWidth, 
		y = CONFIG.thumbnailHeihgt,
		z;
	if(style == "p"){
		z = x;
		x = y;
		y = z;
	}
	/* 
	 * which need further process (remove parts of background)
	 */
	var thumbName = fileName.replace(extNameRE, "_s$1");
	asyncGm(fileName)
		.resize(x, y)
		.writeAsync(thumbName)
		.then(function(){
			winston.info("created thumbnail: " + thumbName);
			workFlow.emit("thumbnailReady", fileName, style);
			if(CONFIG.thumbnailMono){
				workFlow.emit("genMono", thumbName, style);
			}
		})
		.catch(function(err){
			winston.error("at 4th step -- create thumbnail failed: " + err);
		});
	
});

/*
 * 4th step
 * 
 * image crop
 *
 */
workFlow.on("gotSize", function(fileName, size, style){
	if(!CONFIG.crop) return
	/* 
	 * which need further process (remove parts of background)
	 */
	var cropName = fileName.replace(extNameRE, "_crop$1");
	asyncGm(fileName)
		.command('convert')
		.in('-crop', '640x640+0+0')
		.writeAsync(cropName)
		.then(function(){
			winston.info("crop name  " + cropName);
		})
		.catch(function(err){
			winston.error("at 4th step -- create thumbnail failed: " + err);
		});
	
});


/*
 * 4th step
 *
 * check for overlay
 *
 */
workFlow.on("gotSize", function(fileName, size, style){
	if(CONFIG.overlay){
		var overlay = CONFIG.overlay[style] || "";
			overlay = path.join(CONFIG_PATH, overlay);
			asyncGm(overlay)
				.sizeAsync()
				.then(function(overlaySize){
					var newFileName = fileName.replace(extNameRE, "_c$1");
					var aGm = this;
					aGm.getNew(fileName)
						.quality(CONFIG.imageQuality)
						.command('composite')
						.in("-gravity", "center")
						.in("-geometry", size.width + "x" + size.height)
						.in(overlay)
						.writeAsync(newFileName)
						.then(function(){
							winston.info("create an overlay image for " + fileName);
							workFlow.emit('processComplete');
						})
						.catch(function(err){
							winston.error("at 4th step -- overlay inner: " + err);
						});
				})
				.catch(function(err){
					winston.error("at 4th step -- overlay outer: " + err);
				});
		
	}
});

/*
 * 4th step
 * 
 * check watermark
 *
 */
workFlow.on("gotSize", function(fileName, size, style){
	if(Object.prototype.toString.call(CONFIG.watermark) == "[object Array]"){//prefar for make watermark
		setTimeout(function(){
			workFlow.emit('checkWatermark', CONFIG, CONFIG.watermark, fileName, style, size, CONFIG_PATH);
		}, CONFIG.timeout || 0);
	}
});



/* 
 * 3rd step
 *
 * rotate image according to exif
 * then get the width and height 
 * of the image after the rotation
 *
 */
workFlow.on("resized", function(fileName){
	asyncGm(fileName)
		.quality(CONFIG.imageQuality)
		.autoOrient()
		.writeAsync(fileName)
		.then(function(){
			var aGm = this;
			winston.info(fileName + " been autoOriented.");
			aGm.getNew(fileName)
				.sizeAsync()
				.then(function(size){
					winston.info("get image dimension width: " + size.width + " , height: " + size.height);
					var style = "l";
					if(size.width < size.height){
						style = "p";
					}
					if(CONFIG.genQrcode){
						fs.readFile(fileName, function(err, data){
							if(err){
								winston.error(err);
							}else{
								fs.writeFile(NEW_NAME_QR, data, function(err){
									if(err){
										winston.error(err);
									}
								});
							}
						});
					}

					workFlow.emit("gotSize", fileName, size, style);
				})
				.catch(function(err){
					winston.error("at 3rd step inner: " + err);
				});

		})
		.catch(function(err){
			winston.error("at 3rd step outer: " + err);
		});
});

/*
 * 3nd step
 * delete the original image 
 *
 */
 
workFlow.on("resized", function(){
	fs.unlinkAsync(FILE_PATH)
		.catch(function(err){
			winston.info("can't delete original image file.");
		});
});



/* 
 * 1st step
 *
 * read config from cooresponded 'config.json'
 * then check whether the image is complete 
 * jpg end with 'ffd9'
 * png end with '6082'
 * if completed, go further process
 * not, try 30 seconds later, give up after 6 times tried
 */
fs.readFileAsync(path.join(CONFIG_PATH, "config.json"), {encoding: "utf8"})
	.then(function(data){
		return JSON.parse(data);
	})
	.then(function(config){
		CONFIG = append({}, defaultConfig.process, config);
		CONFIG.imageBasePath = CONFIG.imageBasePath + "/" + EVENT_ID + "/";
		NEW_NAME = generateName(CONFIG.nameLength);
		//NEW_NAME = CONFIG.imageBasePath + NEW_NAME + extName;
		NEW_NAME = CONFIG.imageBasePath + NEW_NAME + '.jpg';

		asyncGm(FILE_PATH)
			.sizeAsync()
			.then(function(size){
				winston.info("get original image dimension width: " + size.width + " , height: " + size.height);
			
				var aGm = this;
				var x, y;
				if(size.width > size.height){
					x = CONFIG.imgWidth;
					y = CONFIG.imgHeight;
				}else{
					x = CONFIG.imgHeight;
					y = CONFIG.imgWidth;
				}
				aGm.resize(x, y)
					.quality(CONFIG.imageQuality)
					.writeAsync(NEW_NAME)
					.then(function(){
						winston.info(FILE_PATH + " been resized.")
						workFlow.emit("resized", NEW_NAME);
					})
					.catch(function(err){
						winston.error("at 2nd step inner: " + err);
					});
				
			})
			.catch(function(err){
				winston.error("at 2nd step outer: " + err);
			});
		
		NEW_NAME_C = NEW_NAME.replace(extNameRE, "_c.jpg");
		NEW_NAME_S = NEW_NAME.replace(extNameRE, "_s.jpg");
		NEW_NAME_QR = NEW_NAME.replace(extNameRE, "_qr.jpg");

		//NEW_NAME_C = NEW_NAME.replace(extNameRE, "_c$1");
		//NEW_NAME_S = NEW_NAME.replace(extNameRE, "_s$1");
		//NEW_NAME_QR = NEW_NAME.replace(extNameRE, "_qr$1");
		

		
	})
	.catch(function(err){
		winston.error("at 1st step ourter: " + err);
	});

